package com.sky.his.api.front.service.impl;

import cn.hutool.core.map.MapUtil;
import com.sky.his.api.common.PageUtils;
import com.sky.his.api.db.dao.AppointmentDao;
import com.sky.his.api.db.dao.AppointmentRestrictionDao;
import com.sky.his.api.db.dao.OrderDao;
import com.sky.his.api.db.pojo.AppointmentEntity;
import com.sky.his.api.front.service.AppointmentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("FrontAppointmentServiceImpl")
@Slf4j
public class AppointmentServiceImpl implements AppointmentService {

    @Resource
    private OrderDao orderDao;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private AppointmentDao appointmentDao;

    @Resource
    private AppointmentRestrictionDao appointmentRestrictionDao;

    @Override
    @Transactional
    public String insert(AppointmentEntity entity) {
        HashMap<String, String> resultCode = new HashMap() {
            {
                put("full", "当天预约已满，请选择其他日期");
                put("dail", "预约失败");
                put("success", "预约成功");
            }
        };
        String key = "appointment#" + entity.getDate();
        // 使用 RedisTemplate 执行一个事务操作
        String execute = (String) redisTemplate.execute(new SessionCallback() {
            @Override
            public String execute(RedisOperations operations) throws DataAccessException {
                // 监视 key 键，如果 key 在事务执行前被修改，事务会失败
                operations.watch(key);

                // 获取 key 对应哈希表中的所有字段和值
                Map entry = operations.opsForHash().entries(key);

                // 从哈希表中获取 maxNum 和 realNum 的值
                int maxNum = Integer.parseInt(entry.get("maxNum").toString());  // 最大数量
                int realNum = Integer.parseInt(entry.get("realNum").toString());  // 当前实际数量

                // 判断当前数量是否小于最大数量，如果小于则可以继续操作
                if (realNum < maxNum) {
                    // 开始事务操作，后续的操作会先缓存，不会立即执行
                    operations.multi();

                    // 执行实际的增量操作，增加 realNum 的值
                    operations.opsForHash().increment(key, "realNum", 1);

                    // 提交事务并执行之前的操作
                    List<Boolean> list = operations.exec();

                    // 如果 list 的大小为 0，说明事务没有成功执行（可能是因为被其他操作修改了）
                    if (list.size() == 0) {
                        // 返回失败状态码
                        return resultCode.get("fail");
                    }

                    // 获取事务执行结果，list.get(0) 返回的是增量操作的结果，判断是否成功
                    boolean bool = list.get(0);
                    // 根据操作结果返回成功或失败
                    return resultCode.get(bool ? "success" : "fail");
                } else {
                    // 如果 realNum >= maxNum，则取消监视，不执行事务操作
                    operations.unwatch();
                    // 返回失败状态码
                    return resultCode.get("fail");
                }
            }
        });

        if (!execute.equals(resultCode.get("success"))) {
            return execute;
        }
        int rows = appointmentDao.insert(entity);
        if (rows != 1) {
            return resultCode.get("fail");
        }
        Map entry = redisTemplate.opsForHash().entries(key);
        int maxNum = Integer.parseInt(entry.get("maxNum").toString());
        HashMap param = new HashMap() {
            {
                put("date", entity.getDate());
                put("num_1", maxNum);
                put("num_2", maxNum);
                put("num_3", 1);
            }
        };
        rows = appointmentRestrictionDao.saveOrUpdateRealNum(param);
        if (rows == 0) {
            return resultCode.get("fail");
        }
        int orderId = entity.getOrderId();
        param = new HashMap() {
            {
                put("status", 5);
                put("id", orderId);
            }
        };
        rows = orderDao.updateStatus(param);
        if (rows == 0) {
            return resultCode.get("fail");
        }
        return resultCode.get("success");
    }

    @Override
    public PageUtils searchByPage(Map param) {
        ArrayList<HashMap> list = new ArrayList<>();
        long count = appointmentDao.searchFrontAppointmentCount(param);
        if (count > 0) {
            list = appointmentDao.searchFrontAppointmentByPage(param);
        }
        int page = MapUtil.getInt(param, "page");
        int length = MapUtil.getInt(param, "length");
        PageUtils pageUtils = new PageUtils(list, count, page, length);
        return pageUtils;
    }
}
